static char version[]=
"\nwurmd.c: v20130522 Timothy Demulder <timothy@syphzero.net>, https://code.google.com/p/wurmd/\n";
static char usage[] =
"usage: wurmd [-o <mac>] [-i <ifname>] [-fvd] [-l <config file>] -c <config file>\n";
static char lusage[] =
"\n  This daemon generates and transmits Wake-On-Lan (WoL) packets \n"
"  to configured hosts based on networking events in an attempt to\n"
"  wake up sleeping machines when they're actually needed.\n"
"\nOptions:\n"
"	-o mac		Send one WoL packet to MAC address, once.\n"
"	-i ifname       Use interface IFNAME instead of the system's default.\n"
"	-c conffile     Use the configuration file CONFFILE.\n"
"	-v              Be verbose.\n"
"	-d		Be even more verbose\n"
"	-f		Don't run in the background\n"
"	-l logfile	Use the log file LOGFILE\n"
"			(default: /var/log/wurmd.log)\n";
/*
 * To the nice people who wrote libpcap: thanks.
 * 
 * Copyright 2013, Timothy Demulder <timothy@syphzero.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <arpa/inet.h>
#include <ctype.h>
#include <netinet/ether.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <net/if_arp.h>
#include <pcap/pcap.h>
#include <regex.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "wurmd.h"

#include <resolv.h>
#include <arpa/nameser.h>

char *cnfile, *logfile="/var/log/wurmd.log";
int verbose=0,debug=0,frgrnd=0;

int main(int argc, char *argv[]) {
	char *dev=NULL;
	char *sdteaddr=NULL;
	int c;
	while ((c = getopt (argc, argv, "fdvhl:i:c:o:")) != -1)
	switch (c)
	{
		case 'v':
			verbose=1;
			break;
		case 'd':
			debug=1;
			verbose=1;
			break;
		case 'h':
			printf("%s",usage);
			printf("%s",lusage);
			printf("%s",version);
			return 0;
		case 'i':
			dev=optarg;
			break;
		case 'c':
			cnfile=optarg;
			break;
		case 'f':
			frgrnd=1;
			break;
		case 'l':
			logfile=optarg;
			break;
		case 'o':
			frgrnd=1;
			sdteaddr=optarg;
			break;
		case '?':
			if (optopt == 'c')
				fprintf (stderr, "Option -%c requires an argument.\n", optopt);
			else if (isprint (optopt))
				fprintf (stderr, "Unknown option `-%c'.\n", optopt);
			else
				fprintf (stderr,"Unknown option character `\\x%x'.\n",optopt);
			printf("%s",usage);
			return 1;
		default:
			abort ();
	}
	
	if (getuid()) {
		fprintf(stderr,"%s: This program must be run as uid 0.\n",argv[0]);
		exit(1);
	}
	if (sdteaddr) {
		eaddrval(sdteaddr);
		sndwolpktot(sdteaddr);
		exit(0);
	}

	sichk();

	if (cnfile==NULL)
		eprintf("%s needs a config file to run.\n%s",argv[0],usage);
	FILE *fp;
	fp = fopen(logfile, "a+");
	if(fp==NULL)
		eprintf("Can't open log file %s.\n",logfile);
	fclose(fp);
	if (frgrnd!=1) {
		pid_t pid, sid;
		char *curdir;
		curdir = getcwd(NULL, 0);
		pid = fork(); 
		if (pid < 0) {
			exit(EXIT_FAILURE);
		}
		if (pid > 0) {
			exit(EXIT_SUCCESS);
		}
		umask(0);
		sid = setsid();
		if (sid < 0) {
			exit(EXIT_FAILURE);
		}
		if ((chdir(curdir)) < 0) {
			eprintf("Could not change working directory to %s\n",curdir);
			exit(EXIT_FAILURE);
		}
		dprintf("Current working dir: %s\n",curdir);
		free(curdir);
		close(STDIN_FILENO);
		close(STDOUT_FILENO);
	}
	
	signal(SIGINT, sighndl);
	
	char errbuf[PCAP_ERRBUF_SIZE];
	if (dev == NULL) {
		dev = pcap_lookupdev(errbuf);
		if (dev == NULL)
			eprintf("Couldn't find default device: %s\n", errbuf);
		vprintf("No interface option provided, using %s\n",dev);
	}
	bpf_u_int32 mask,net;
	if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1)
		eprintf("Couldn't get ip address for device %s\n", errbuf);

	pcap_if_t *devs, *dvc;
	pcap_addr_t *addr;
	
	char *pcapfltrprt = "(arp and (";
	char *pcapfltr = smalloc(sizeof(char)*strlen(pcapfltrprt)+1);
	strcpy(pcapfltr,pcapfltrprt);
	
	char *pcapfltrprtudp = " or (udp dst port 137 ";
	char *pcapfltrudp = smalloc(sizeof(char)*strlen(pcapfltrprtudp)+1);
	strcpy(pcapfltrudp,pcapfltrprtudp);
	
	char *pcapfltrsynprt=" or (tcp[tcpflags] == tcp-syn and (";
	char *pcapfltrsyn=smalloc(sizeof(char)*strlen(pcapfltrsynprt)+1);
	strcpy(pcapfltrsyn,pcapfltrsynprt);
	
	if (pcap_findalldevs(&devs, errbuf))
		eprintf("Couldn't iterate over all network devices: %s\n", errbuf);
	dvc = devs;
	int cntr=0;
	
	while(dvc != NULL) {
		if (*dvc->name == *dev) {
			addr=dvc->addresses;
			while (addr != NULL) {
				if(addr->addr->sa_family == AF_INET) {
					if (cntr) {
						pcapfltrprt=" or ";
						pcapfltr = srealloc(pcapfltr,pcapfltrprt);
						strcat(pcapfltr,pcapfltrprt);
						
						pcapfltrprtudp=" or ";
						pcapfltr = srealloc(pcapfltrudp,pcapfltrprtudp);
						strcat(pcapfltr,pcapfltrprt);
						
						pcapfltrsynprt=" or ";
						pcapfltr = srealloc(pcapfltrsyn,pcapfltrsynprt);
						strcat(pcapfltrsyn,pcapfltrsynprt);
					}
					char taddrbuf[INET_ADDRSTRLEN];
					inet_ntop(AF_INET,&(((struct sockaddr_in*)addr->addr)->sin_addr),taddrbuf,INET_ADDRSTRLEN);
					char *taddr = taddrbuf;
					taddrval(taddr);
					char *taddrh;
					
					pcapfltrsynprt="src ";
					pcapfltrsyn = srealloc(pcapfltrsyn,pcapfltrsynprt);
					strcat(pcapfltrsyn,pcapfltrsynprt);
					pcapfltrsyn = srealloc(pcapfltrsyn,taddr);
					strcat(pcapfltrsyn,taddr);
					pcapfltrprtudp="and src ";
					pcapfltrudp = srealloc(pcapfltrudp,pcapfltrprtudp);
					strcat(pcapfltrudp,pcapfltrprtudp);
					pcapfltrudp = srealloc(pcapfltrudp,taddr);
					strcat(pcapfltrudp,taddr);
					taddrh = dtohaddr(taddr);
					pcapfltrprt="ether[28:4]=";
					pcapfltr = srealloc(pcapfltr,pcapfltrprt);
					strcat(pcapfltr,pcapfltrprt);
					pcapfltr = srealloc(pcapfltr,taddrh);
					strcat(pcapfltr,taddrh);
					free(taddrh);
					cntr++;
				}
				addr=addr->next;
			}
		}
		dvc = dvc->next;
	}
	pcap_freealldevs(devs);
	
	char *bfr,*taddr,*eaddr;
	static int rdbfr = 96;
	bfr = smalloc(sizeof(char)*rdbfr);
	taddr = smalloc(sizeof(char)*15);
	eaddr = smalloc(sizeof(char)*18);
	cntr=0;
	fp = fopen(cnfile, "r");
	if(fp==NULL)
		eprintf("Can't open configuration file %s.\n",cnfile);
	pcapfltrprt=") and (";
	pcapfltr = srealloc(pcapfltr,pcapfltrprt);
	strcat(pcapfltr,pcapfltrprt);
	
	pcapfltrsynprt=") and (";
	pcapfltrsyn = srealloc(pcapfltrsyn,pcapfltrsynprt);
	strcat(pcapfltrsyn,pcapfltrsynprt);
	
	while(fgets(bfr, rdbfr, fp)!=NULL) {
		if (!strncmp(bfr,"#",1) || !strncmp(bfr,"\n",1))
			continue;
		if (cntr) {
			pcapfltrprt=" or ";
			pcapfltr = srealloc(pcapfltr,pcapfltrprt);
			strcat(pcapfltr,pcapfltrprt);
			
			pcapfltrsynprt=" or ";
			pcapfltrsyn = srealloc(pcapfltrsyn,pcapfltrsynprt);
			strcat(pcapfltrsyn,pcapfltrsynprt);
		}
		sscanf(bfr,"%s %s", taddr,eaddr);
		taddrval(taddr);
		eaddrval(eaddr);
		
		pcapfltrsynprt="dst ";
		pcapfltrsyn = srealloc(pcapfltrsyn,pcapfltrsynprt);
		strcat(pcapfltrsyn,pcapfltrsynprt);
		pcapfltrsyn = srealloc(pcapfltrsyn,taddr);
		strcat(pcapfltrsyn,taddr);
		
		pcapfltrprt="ether[38:4]=";
		pcapfltr = srealloc(pcapfltr,pcapfltrprt);
		strcat(pcapfltr,pcapfltrprt);
		char *taddrh;
		taddrh = dtohaddr(taddr);
		pcapfltr = srealloc(pcapfltr,taddrh);
		strcat(pcapfltr,taddrh);
		free(taddrh);
		cntr++;
	}
	if (cntr==0) {
		eprintf("No hosts configured in the configuration file.\n");
	}
	fclose(fp);
	pcapfltrprt="))";
	pcapfltr = srealloc(pcapfltr,pcapfltrprt);
	strcat(pcapfltr,pcapfltrprt);
	
	pcapfltrprtudp=")";
	pcapfltrudp=srealloc(pcapfltrudp,pcapfltrprtudp);
	strcat(pcapfltrudp,pcapfltrprtudp);
	
	pcapfltrsynprt="))";
	pcapfltrsyn=srealloc(pcapfltrsyn,pcapfltrsynprt);
	strcat(pcapfltrsyn,pcapfltrsynprt);
	
	pcapfltr=srealloc(pcapfltr,pcapfltrudp);
	strcat(pcapfltr,pcapfltrudp);
	
	pcapfltr=srealloc(pcapfltr,pcapfltrsyn);
	strcat(pcapfltr,pcapfltrsyn);
	
	free(bfr);
	free(taddr);
	free(eaddr);
	
	const int pcappromisc=1;
	const int pcaptimeout=1000;
	pcap_t *session_handle;
	session_handle = pcap_open_live(dev, BUFSIZ, pcappromisc, pcaptimeout, errbuf);
	if (session_handle == NULL)
		eprintf("Couldn't open device %s: %s\n", dev, errbuf);

	struct bpf_program ftrp;
	memset(&ftrp,0,sizeof(struct bpf_program));
	if (pcap_compile(session_handle, &ftrp, pcapfltr, 0, net) == -1)
		eprintf("Couldn't parse filter %s: %s\n", pcapfltr, pcap_geterr(session_handle));
	if (pcap_setfilter(session_handle, &ftrp) == -1)
		eprintf("Couldn't install filter %s: %s\n", pcapfltr, pcap_geterr(session_handle));
	vprintf("Working the magic on %s\n",dev);
	dprintf("Pcap filter: %s\n",pcapfltr);
	free(pcapfltr);
	const int pktamount = -1;
	pcap_loop(session_handle,pktamount,pktmatch,NULL);
	return(0);
}
